package com.java.base.exercise.current.selfcache.imooccache2;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @Author RenPu
 * @Date 2021/9/20 20:41
 * @Version 1.0
 * @Description: 使用ConcurrentHashMap替代hashMap，进行优化，
 * 缺点: ConcurrentHashMap会存在重复计算，请求没有及时核算出结果
 * 缺点优化：使用Future+Callable，避免重计算
 **/
public class ImoocCache3<A, V> implements Comtable<A, V> {

    //private final Map<A, V> cache=new ConcurrentHashMap<>();

    //创建定时线程池，用于清除缓存到期的数据
    public final static ScheduledExecutorService s = Executors.newScheduledThreadPool(5);

    //缓存容器
    private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();


    private Comtable<A, V> c;

    public ImoocCache3(Comtable<A, V> c) {
        this.c = c;
    }

    @Override
    public V compter(A arg) throws InterruptedException, ExecutionException {
        while (true) {
            Future<V> f = cache.get(arg);
            if (f == null) {
                //创建Callable对象，进行异步调用计算方法
                Callable<V> callable = new Callable<V>() {
                    @Override
                    public V call() throws Exception {
                        return c.compter(arg);
                    }
                };
                FutureTask<V> ft = new FutureTask<V>(callable);
                //优化前
//            f=ft; //防止方法第一步，f==null成立
//            cache.put(arg,ft);  //首先把ft放入缓存，此时并没有计算方法的调用
//            System.out.println("从FutureTask调用了计算函数");
//            ft.run();           //开始调用计算方法

                /*--------------------优化后----------------*/
                f = cache.putIfAbsent(arg, ft);// 利用原子性，防止小概率的重复计算更好的提高领用率
                if (f == null) {
                    f = ft;
                    System.out.println("从FutureTask调用了计算函数");
                    ft.run();
                }

            }

            //异常处理
            try {
                return f.get();         //如果没有值，将进行一直阻塞获取数据

            } catch (CancellationException e) {
                System.out.println("被取消了！！！！！！！");
                cache.remove(arg);
                throw e;
            } catch (InterruptedException e) {
                cache.remove(arg);
                throw e;
            } catch (ExecutionException e) {
                System.out.println("计算出错,正在重试！！！！！！");
                cache.remove(arg);
                throw e;
            }
        }

    }


    /**
     * 设置随机-----过期时间方法
     * 防止大部分的过期时间，都集中同一个时间点周围不均匀，就会导致大量缓存失效，
     * 此时如果有较大的并发会对数据库造，cpu,造成很大的压力，从而导致缓存雪崩
     *
     * @param arg
     * @return
     */
    public V compterRandomExpire(A arg) throws ExecutionException, InterruptedException {
        long randomExpire = (long) (Math.random() * 10000);
        return compter(arg, randomExpire);
    }


    /**
     * 重写compter方法，增加定时清除缓存功能
     *
     * @param arg
     * @param expireTime
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public V compter(A arg, long expireTime) throws ExecutionException, InterruptedException {
        if (expireTime > 0) {
            s.schedule(new Runnable() {
                @Override
                public void run() {
                    //具体清除缓存数据的方法
                    expire(arg);
                }
            }, expireTime, TimeUnit.MILLISECONDS);
        }
        return compter(arg);

    }


    /**
     * 具体清除缓存数据的方法
     *
     * @param arg
     */
    private synchronized void expire(A arg) {
        Future<V> vFuture = cache.get(arg);

        if (vFuture != null) {

            //防止有些任务，在设置的时间周期没有执行完毕，在此直接进行取消
            if (!vFuture.isDone()) {
                System.out.println("Future任务，被取消");
                vFuture.cancel(true);
            }
            System.out.println("过期时间到，缓存被清除！！！！！");
            cache.remove(arg);
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // ImoocCache3 <String,Integer> imoocCache3 = new ImoocCache3<>(new ExceptionFuncation());
        ImoocCache3<String, Integer> imoocCache3 = new ImoocCache3<>(new MyFail());
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Integer result = imoocCache3.compter("666", 5000L);
                    System.out.println("第一次获取数据,结果值为：" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();


        //验证3.0缓存框架
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Integer result = imoocCache3.compter("668");
                    System.out.println("第二次获取数据,结果值为：" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //验证2.0缓存框架，使用synchronized关键字影响性能的差异
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Integer result = imoocCache3.compter("666");
                    System.out.println("第三次获取数据，结果值为：" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
//        Future<Integer> integerFuture = imoocCache3.cache.get("667");
//        integerFuture.cancel(true);

        Thread.sleep(6000L);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Integer result = imoocCache3.compter("666");
                    System.out.println("第四次获取缓存数据，结果值为：" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();


    }

}
